仓颉函数调用流程详解.md•19.3 kB
# 增量引擎:函数调用流程详解
> 本文档详细追踪增量引擎中关键函数的调用链,特别关注 lambda 表达式的创建时机与执行时机。
**基于**: Cangjie/KoalaRuntime 实现
**核心文件**: `runtime/src/core/StateManagerImpl.cj`, `runtime/src/core/ScopeImpl.cj`, `runtime/src/core/memo.cj`
---
## 目录
1. [memoRoot 创建阶段完整流程](#1-memoroot-创建阶段完整流程)
2. [getValue 触发时的完整流程](#2-getvalue-触发时的完整流程)
3. [Lambda 表达式执行时机总结](#3-lambda-表达式执行时机总结)
---
## 1. memoRoot 创建阶段完整流程
### 1.1 调用链概览
```
用户代码: memoRoot(rootNode, update)
↓
memoRoot(node, update)
↓
GlobalStateManager.instance()
↓
StateManagerImpl.updatableNode(node, lambda1, None)
↓
ScopeImpl.init(...) [创建根作用域,传入 lambda2 作为 compute]
↓
返回 ComputableState<Node>(尚未执行计算)
```
### 1.2 逐步执行分析
#### 步骤 1: memoRoot 被调用
```29:32:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj
public func memoRoot<Node>(node: Node, update: (StateManagerImpl) -> Unit): ComputableState<Node> where Node <: IncrementalNode {
let manager = GlobalStateManager.instance()
manager.updatableNode(node, { => manager.runWithFrozen(update)}, None)
}
```
**执行内容**:
1. `GlobalStateManager.instance()` → 获取或创建 `StateManagerImpl`
2. **创建 lambda1**: `{ => manager.runWithFrozen(update) }`
- **注意**: 此时 lambda 只是被创建,**尚未执行**
- `update` 参数被捕获在闭包中
3. 调用 `updatableNode(node, lambda1, None)`
**返回**: `ComputableState<Node>`(根作用域,但此时尚未计算)
---
#### 步骤 2: updatableNode 被调用
```165:184:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj
public func updatableNode<Node>(node: Node, update: () -> Unit, cleanup: ?() -> Unit): ComputableState<Node> where Node <: IncrementalNode {
this.checkForStateComputing()
let scope = ScopeImpl<Node>(
None,
2,
{
=>
update()
node
},
{_ => cleanup?()},
None
)
scope.manager = this
scope.nodeAttached = node
scope.nodeRef = node
scope.param(0, {=> dumpHierarchyOf(scope)}, CONTEXT_ROOT_SCOPE_HIERARCHY_SUPPLIER, true)
scope.param(1, {=> dumpHierarchyOf(node)}, CONTEXT_ROOT_NODE_HIERARCHY_SUPPLIER, true)
return scope
}
```
**执行内容**:
1. `checkForStateComputing()` → 检查是否在计算中(不应在计算中创建根节点)
2. **创建 lambda2**: `{ => update(); node }`
- 捕获了 `update`(即 lambda1)和 `node`
- **注意**: 此时 lambda2 只是被创建,**尚未执行**
3. **创建 lambda3**: `{_ => cleanup?()}`
- 捕获了 `cleanup`(这里是 `None`)
- **注意**: 此时 lambda3 只是被创建,**尚未执行**
4. **调用 `ScopeImpl.init(None, 2, lambda2, lambda3, None)`**:
```353:358:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
init(id: ?Hashscopeid, paramCount: Int64, compute: ?() -> Value, cleanup: ?(value: ?Value) -> Unit, reuseKey: ?String) {
super(id, paramCount)
this.myCompute = compute // 保存 lambda2,但不执行
this.myCleanup = cleanup // 保存 lambda3,但不执行
this.myreuseKey = reuseKey
}
```
- `super(id, paramCount)` → 初始化父类 `ManagedScope`
- `this.myCompute = lambda2` → 保存 lambda,**不执行**
- `this.myCleanup = lambda3` → 保存 lambda,**不执行**
5. **设置作用域属性**:
- `scope.manager = this` → 关联管理器
- `scope.nodeAttached = node` → 直接设置节点(不需要 lambda)
- `scope.nodeRef = node` → 设置节点引用
6. **调用 `param(0, lambda4, ...)`**:
```181:181:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj
scope.param(0, {=> dumpHierarchyOf(scope)}, CONTEXT_ROOT_SCOPE_HIERARCHY_SUPPLIER, true)
```
- **创建 lambda4**: `{=> dumpHierarchyOf(scope)}`
- **注意**: lambda4 被传入 `param`,但**此时不执行**
- 在 `param` 中创建 `ParameterImpl`,lambda4 被保存为参数值
7. **调用 `param(1, lambda5, ...)`**:
```182:182:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj
scope.param(1, {=> dumpHierarchyOf(node)}, CONTEXT_ROOT_NODE_HIERARCHY_SUPPLIER, true)
```
- **创建 lambda5**: `{=> dumpHierarchyOf(node)}`
- **注意**: lambda5 被传入 `param`,但**此时不执行**
8. **返回 `scope`**(根作用域)
**关键点**:
- 所有 lambda 在此阶段只是被创建和保存,**均未执行**
- `recomputeNeeded = true`(默认值,首次需要计算)
---
### 1.3 param 函数执行细节
```220:238:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
public func param<Value>(index: Int64, value: Value, name: ?String, contextLocal: Bool): State<Value> {
let params = this.params.getOrThrow()
let manager = this.manager.getOrThrow()
if (let Some(param) <- params[index]) {
let state = (param as ParameterImpl<Value>).getOrThrow()
if (contextLocal && !this.hasNamedState(name.getOrThrow(), params[index])) {
throw IllegalArgumentException("name was unexpectedly changed to " + name.getOrThrow())
}
state.setValue(value)
return state
} else {
let state = ParameterImpl<Value>(value, name, manager)
params[index] = state
if (contextLocal) {
this.setNamedState(name.getOrThrow(), state)
}
return state
}
}
```
**执行内容**(首次调用,`params[index]` 为 `None`):
1. 创建 `ParameterImpl<Value>(value, name, manager)`
- `value` 可能是 lambda(如 `{=> dumpHierarchyOf(scope)}`)
- **注意**: lambda 作为值被保存,**不执行**
2. `params[index] = state` → 保存参数状态
3. 返回 `State<Value>`
---
## 2. getValue 触发时的完整流程
### 2.1 调用链概览
```
用户代码: rootScope.getValue() 或 访问 .value
↓
ScopeImpl.getValue()
↓
ScopeImpl.isUnchanged()
↓ (如果是首次/需要重算)
设置 manager.currentScope = this
↓
ScopeImpl.isUnchanged() 返回 false
↓
ScopeImpl.getValue() 继续
↓
执行 lambda2: myCompute() [即 { => update(); node }]
↓
├─ 执行 lambda1: manager.runWithFrozen(update)
↓ ├─ frozen = true
↓ ├─ 执行用户传入的 update(manager) [真正的用户代码]
↓ └─ frozen = old
└─ 返回 node
↓
ScopeImpl.recache(node)
↓
├─ 执行 param(0).getValue() → 执行 lambda4: dumpHierarchyOf(scope)
├─ 执行 param(1).getValue() → 执行 lambda5: dumpHierarchyOf(node)
├─ detachChildScopes(None)
├─ parent?.increment(...)
└─ nodeAttached?.incrementalUpdateDone(...)
↓
ScopeImpl.getCached()
↓
返回 node
```
### 2.2 逐步执行分析
#### 步骤 1: getValue 被调用
```371:378:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
public func getValue(): Value {
if (this.isUnchanged()) {
this.getCached()
} else {
let compute = this.myCompute.getOrThrow()
this.recache(compute())
}
}
```
**执行内容**:
1. 调用 `isUnchanged()` → 判断是否需要重新计算
---
#### 步骤 2: isUnchanged 被调用(首次/需要重算)
```384:406:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
public func isUnchanged(): Bool {
if (this.isRecomputeNeeded()) {
this.incremental = None
this.nodeCount = 0
if (!this.isDisposed()) {
if(let Some(manager) <- this.manager) {
this.scopeInternal = manager.currentScope // Record scopeInternal for recache
manager.currentScope = this
}
}
return false
} else {
this.parent?.increment(
if (this.nodeAttached.isSome()) {
1
} else {
this.nodeCount
},
true
)
return true
}
}
```
**执行内容**(首次调用,`recomputeNeeded = true`):
1. `isRecomputeNeeded()` → 返回 `true`
2. `this.incremental = None` → 清空增量引用
3. `this.nodeCount = 0` → 重置节点计数
4. **保存当前作用域**: `this.scopeInternal = manager.currentScope`
5. **切换当前作用域**: `manager.currentScope = this`
- **关键**: 此时后续的状态访问会在此作用域下登记依赖
6. **返回 `false`** → 表示需要重新计算
---
#### 步骤 3: getValue 继续执行(计算分支)
```371:378:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
public func getValue(): Value {
if (this.isUnchanged()) {
this.getCached()
} else {
let compute = this.myCompute.getOrThrow() // 获取 lambda2
this.recache(compute()) // 执行 lambda2
}
}
```
**执行内容**:
1. `this.myCompute.getOrThrow()` → 获取保存的 lambda2: `{ => update(); node }`
2. **执行 lambda2**: `compute()`
- **此时才真正执行 lambda2**
---
#### 步骤 4: lambda2 执行(lambda1 被调用)
lambda2 的内容:
```170:174:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj
{
=>
update() // 这是 lambda1: { => manager.runWithFrozen(update) }
node
}
```
**执行内容**:
1. **执行 `update()`** → 这是 lambda1: `{ => manager.runWithFrozen(update) }`
- **此时才真正执行 lambda1**
---
#### 步骤 5: lambda1 执行(runWithFrozen)
lambda1 的内容:
```55:60:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/StateManagerImpl.cj
func runWithFrozen(runnable: (StateManagerImpl) -> Unit): Unit {
let old = frozen
frozen = true // states are frozen during recomposition
runnable(this) // 执行用户传入的 update(manager)
frozen = old
}
```
**执行内容**:
1. `let old = frozen` → 保存旧的冻结状态(通常是 `false`)
2. `frozen = true` → 设置冻结标志(防止状态在重组期间被修改)
3. **执行 `runnable(this)`** → 这是用户传入的 `update(manager)`
- **此时才执行用户的 update 函数**
- 在 `update` 中,用户代码可能调用 `memo`、`NodeAttach` 等,这些会创建子作用域并可能触发计算
4. `frozen = old` → 恢复冻结状态
5. **返回**(lambda1 执行完成)
---
#### 步骤 6: lambda2 继续(返回 node)
lambda2 继续执行:
```
update() // 已完成
node // 返回 node
```
**返回**: `node`(根节点)
---
#### 步骤 7: recache 被调用
```416:438:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
public func recache(newValue: Value): Value {
if (!this.isDisposed()) {
if(let Some(manager) <- this.manager) {
manager.currentScope = this.scopeInternal // 恢复之前的作用域
}
}
let oldValue = this.myValue
this.myValue = newValue
this.myModified = this.myComputed && !equalValues(newValue, oldValue.getOrThrow())
this.myComputed = true
this.recomputeNeeded = false
this.detachChildScopes(None)
this.parent?.increment(
if (this.nodeAttached.isSome()) {
1
} else {
this.nodeCount
},
false
)
this.nodeAttached?.incrementalUpdateDone(this.parent?.nodeRef ?? None)
return this.getCached()
}
```
**执行内容**:
1. **恢复作用域**: `manager.currentScope = this.scopeInternal`
- 恢复到进入计算前的状态
2. **更新缓存值**:
- `this.myValue = newValue` → 保存计算结果(node)
- `this.myComputed = true` → 标记已计算
- `this.recomputeNeeded = false` → 标记不需要重算
3. `detachChildScopes(None)` → 断开失效的子作用域
4. `this.parent?.increment(...)` → 通知父作用域更新节点计数
5. `this.nodeAttached?.incrementalUpdateDone(...)` → 节点增量更新完成
6. **调用 `getCached()`** → 获取缓存值并可能触发参数 lambda 执行
---
#### 步骤 8: getCached 被调用
```408:414:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/ScopeImpl.cj
public func getCached(): Value {
if(let Some(dep) <- this.manager?.getDependency()) {
this.dependencies?.register(dep)
}
this.dependencies?.onUpdate(this.myModified)
this.myValue.getOrThrow()
}
```
**执行内容**:
1. 登记依赖(如果有当前依赖)
2. `this.dependencies?.onUpdate(this.myModified)` → 更新依赖关系
3. **返回 `this.myValue.getOrThrow()`** → 返回缓存的 node
**注意**: 在根作用域中,参数(param(0) 和 param(1))的 lambda(lambda4 和 lambda5)可能在以下情况被执行:
- 当参数被访问时(如 `param(0).getValue()`)
- 在依赖更新或其他需要评估参数值时
---
### 2.3 子作用域创建流程(在 update 中)
当用户的 `update` 函数执行时,可能调用 `memo` 或 `NodeAttach`,这些会创建子作用域。
#### 示例: memo2 调用流程
```48:56:/home/gloria/Cangjie/incremental_runtime/runtime/src/core/memo.cj
func memo2<P1, P2, Value>(p1: P1, p2: P2, compute: (__memo_key: Hashscopeid, manager: StateManagerImpl, s1: State<P1>, s2: State<P2>) -> Value): Value {
let scope = manager.getMemoScope<Value>(_new_id, 2, None, None, None, false, None)
let s1 = scope.param(0, p1)
let s2 = scope.param(1, p2)
if (scope.isUnchanged()) {
scope.getCached()
} else {
scope.recache(compute(s1, s2))
}
}
```
**执行内容**:
1. `manager.getMemoScope<Value>(...)` → 获取或创建子作用域
- 调用 `getChildScope`,如果未找到则创建新的 `ScopeImpl`
- **注意**: `compute` 参数为 `None`,此时不传入计算函数
2. `scope.param(0, p1)` → 创建参数状态
- 创建 `ParameterImpl`,保存 `p1` 值
- **注意**: `p1` 可能是值,也可能是 lambda,但**不执行**
3. `scope.param(1, p2)` → 创建参数状态
4. `scope.isUnchanged()` → 判断是否需要重算
- 首次调用返回 `false`(需要重算)
5. **执行 `compute(s1, s2)`** → 这是用户传入的计算函数
- **此时才真正执行用户的 compute lambda**
- `s1` 和 `s2` 是 `State` 对象,访问时会触发依赖登记
6. `scope.recache(compute(s1, s2))` → 缓存计算结果
---
## 3. Lambda 表达式执行时机总结
### 3.1 创建阶段(不执行)
以下 lambda 在创建阶段被创建和保存,**但不执行**:
| Lambda | 创建位置 | 保存位置 | 执行时机 |
|--------|---------|---------|---------|
| `lambda1`: `{ => manager.runWithFrozen(update) }` | `memoRoot` | `updatableNode` 的 `update` 参数 | `getValue()` 时 |
| `lambda2`: `{ => update(); node }` | `updatableNode` | `ScopeImpl.myCompute` | `getValue()` → `compute()` 时 |
| `lambda3`: `{_ => cleanup?()}` | `updatableNode` | `ScopeImpl.myCleanup` | 作用域销毁时 |
| `lambda4`: `{=> dumpHierarchyOf(scope)}` | `updatableNode` | `ParameterImpl.value` | 参数被访问时 |
| `lambda5`: `{=> dumpHierarchyOf(node)}` | `updatableNode` | `ParameterImpl.value` | 参数被访问时 |
| `用户 update`: `(manager) => { ... }` | 用户代码 | 捕获在 lambda1 中 | lambda1 执行时 |
| `用户 compute`: `(key, mgr, s1, s2) => { ... }` | 用户代码 | 传入 `memo2` 等 | `memo2` 中调用时 |
### 3.2 执行时机总结
| 执行阶段 | 触发的 Lambda | 调用路径 |
|---------|-------------|---------|
| **创建阶段** | 无 | 所有 lambda 只是被创建和保存 |
| **getValue() 触发** | lambda2 (`myCompute`) | `getValue()` → `compute()` |
| **lambda2 执行** | lambda1 (`runWithFrozen`) | `lambda2` → `update()` |
| **lambda1 执行** | 用户 `update` | `lambda1` → `runnable(this)` |
| **用户 update 中** | `memo`/`NodeAttach` 的 compute | `memo2` → `compute(s1, s2)` |
| **参数访问** | 参数 lambda(如 lambda4/lambda5) | `param.getValue()` 时 |
### 3.3 关键原则
1. **延迟执行**: Lambda 在创建时不执行,只在需要时才执行
2. **作用域切换**: 在执行 lambda 前,`manager.currentScope` 被设置为当前作用域
3. **冻结保护**: 在重组期间(`frozen=true`),状态修改被禁止
4. **依赖登记**: 在 lambda 执行期间访问状态时,自动登记依赖关系
---
## 4. 完整示例:创建到首次计算的完整流程
### 场景设置
```text
let counter = mutableState(0)
let rootNode = IncrementalNode()
let rootScope = memoRoot(rootNode, (manager) => {
// 组件A
NodeAttach(
() => IncrementalNode(),
(nodeA) => {
let c = counter.getValue()
println("A: ${c}")
}
)
})
```
### 完整调用链
```
1. memoRoot(rootNode, update)
├─ GlobalStateManager.instance() → 返回 manager
├─ 创建 lambda1: { => manager.runWithFrozen(update) }
└─ updatableNode(rootNode, lambda1, None)
├─ 创建 lambda2: { => update(); node }
├─ 创建 lambda3: {_ => cleanup?()}
├─ ScopeImpl.init(None, 2, lambda2, lambda3, None)
│ ├─ super(None, 2)
│ ├─ this.myCompute = lambda2 [保存,不执行]
│ └─ this.myCleanup = lambda3 [保存,不执行]
├─ scope.param(0, lambda4, ...) [lambda4 保存,不执行]
├─ scope.param(1, lambda5, ...) [lambda5 保存,不执行]
└─ 返回 scope [根作用域创建完成]
2. 首次访问 rootScope.getValue()
├─ ScopeImpl.getValue()
│ ├─ isUnchanged()
│ │ ├─ isRecomputeNeeded() → true
│ │ ├─ this.scopeInternal = manager.currentScope [保存]
│ │ ├─ manager.currentScope = this [切换]
│ │ └─ 返回 false
│ ├─ compute = this.myCompute.getOrThrow() [获取 lambda2]
│ └─ recache(compute()) [执行 lambda2]
│ ├─ 执行 lambda2: { => update(); node }
│ │ └─ 执行 update() [即 lambda1]
│ │ ├─ frozen = true
│ │ ├─ 执行 runnable(this) [即用户 update]
│ │ │ └─ NodeAttach(createA, updateA)
│ │ │ ├─ getMemoScope(...) [创建子作用域]
│ │ │ ├─ createA() → 创建 nodeA [执行 create lambda]
│ │ │ ├─ updateA(nodeA) [执行 update lambda]
│ │ │ │ └─ counter.getValue()
│ │ │ │ └─ counter.onAccess() [登记依赖]
│ │ │ └─ recache(nodeA)
│ │ └─ frozen = old
│ │ └─ 返回 node
│ ├─ manager.currentScope = this.scopeInternal [恢复]
│ ├─ this.myValue = node
│ ├─ this.recomputeNeeded = false
│ ├─ detachChildScopes(None)
│ ├─ parent?.increment(...)
│ ├─ nodeAttached?.incrementalUpdateDone(...)
│ └─ getCached()
│ └─ 返回 node
└─ 返回 node
```